﻿using System.Collections.Generic;

using Vestras.StarCraft2.Luna.Parser;
using Vestras.StarCraft2.Luna.Parser.Ast;

namespace Vestras.StarCraft2.Luna.Validation {
    public static class EntityExtensionMethods {
        public static bool IsArray(this TypeReference typeReference) {
            Typedef dummy = null;
            return typeReference.IsArray(out dummy);
        }

        public static IEnumerable<Entity> GetPotentialChildren(this Entity entity) {
            if (entity is Function) {
                return ((Function)entity).Children;
            } else if (entity is Struct) {
                return ((Struct)entity).Children;
            } else if (entity is Statement && ((Statement)entity).CanHaveBlock) {
                return ((Statement)entity).Children;
            }

            return null;
        }

        public static bool HasReturnStatement(this Entity entity) {
            IEnumerable<Entity> children = entity.GetPotentialChildren();
            if (children != null) {
                foreach (Entity child in children) {
                    if (child is ReturnStatement) {
                        return true;
                    }
                }
            }

            return false;
        }

        public static IfStatement GetIfStatement(this ElseStatement elseStatement) {
            foreach (Entity child in elseStatement.Children) {
                if (child is IfStatement) {
                    return child as IfStatement;
                }
            }

            return null;
        }

        private static ElseStatement GetElseStatementWithoutIfStatementChild(ElseStatement elseStatement) {
            IfStatement childIf = elseStatement.GetIfStatement();
            if (childIf == null) {
                return elseStatement;
            } else if (childIf.ElseStatement != null) {
                return GetElseStatementWithoutIfStatementChild(childIf.ElseStatement);
            }

            return null;
        }

        private static bool HasChildValidIfStatementOrWhileStatement(Statement statement) {
            if (statement is IfStatement || statement is WhileStatement) {
                foreach (Entity child in statement.Children) {
                    if (child is IfStatement) {
                        return ((IfStatement)child).HasValidReturnStatement();
                    } else if (child is WhileStatement) {
                        return ((WhileStatement)child).HasValidReturnStatement();
                    }
                }
            }

            return false;
        }

        public static bool HasValidReturnStatement(this Entity entity) {
            if (entity is Function) {
                Function function = entity as Function;
                bool result = false;
                if (function.HasReturnStatement()) {
                    result = true;
                } else {
                    foreach (Entity child in function.Children) {
                        if (child is WhileStatement || child is IfStatement) {
                            if (child.HasValidReturnStatement()) {
                                result = true;
                                break;
                            }
                        }
                    }
                }

                return result;
            } else if (entity is IfStatement) {
                IfStatement ifStatement = entity as IfStatement;
                if (ifStatement.Condition.GetQualifiedId() == "true" && (HasReturnStatement(ifStatement) || HasChildValidIfStatementOrWhileStatement(ifStatement))) {
                    return true;
                } else {
                    ElseStatement startElseStatement = ifStatement.ElseStatement;
                    if (startElseStatement != null) {
                        ElseStatement elseStatementWithoutIfStatementChild = GetElseStatementWithoutIfStatementChild(startElseStatement);
                        if (elseStatementWithoutIfStatementChild != null && elseStatementWithoutIfStatementChild.HasReturnStatement()) {
                            return true;
                        }
                    }
                }
            } else if (entity is WhileStatement) {
                WhileStatement whileStatement = entity as WhileStatement;
                if (whileStatement.Condition.GetQualifiedId() == "true" && (HasReturnStatement(whileStatement) || HasChildValidIfStatementOrWhileStatement(whileStatement))) {
                    return true;
                }
            }

            return false;
        }

        public static bool IsArray(this TypeReference typeReference, out Typedef typedef) {
            typedef = null;
            if (typeReference.ArrayDimensionExpressions.Count > 0) {
                return true;
            } else {
                IList<Entity> list = new List<Entity>(ScopeEntityFilterer.FilterEntities(NamedEntityRegistre.GetEntitiesWithName(typeReference.GetQualifiedId()), typeReference));
                if (list == null) {
                    return false;
                }

                List<Entity> entitiesToRemove = new List<Entity>();
                foreach (Entity entity in list) {
                    if (!(entity is Typedef)) {
                        entitiesToRemove.Add(entity);
                    }
                }

                entitiesToRemove.ForEach(item => list.Remove(item));
                if (list.Count == 0) {
                    return false;
                }

                typedef = list[0] as Typedef;
                if (typedef == null) {
                    return false;
                }

                if (typedef.OriginalType.ArrayDimensionExpressions.Count > 0) {
                    return true;
                }
            }

            return false;
        }

        public static TEntity GetLogicalParentOfType<TEntity>(this Entity entity)
            where TEntity : Entity {
            if (entity.Parent is TEntity) {
                return (TEntity)entity.Parent;
            } else if (entity.Parent != null) {
                return GetLogicalParentOfType<TEntity>(entity.Parent);
            }

            return default(TEntity);
        }

        public static bool IsLogicalChildOfType<TEntity>(this Entity entity)
            where TEntity : Entity {
            return GetLogicalParentOfType<TEntity>(entity) != null;
        }

        public static string GetPotentialName(this Entity entity) {
            if (entity is Function) {
                return ((Function)entity).Name;
            } else if (entity is Variable) {
                return ((Variable)entity).Name;
            } else if (entity is Struct) {
                return ((Struct)entity).Name;
            } else if (entity is Typedef) {
                return ((Typedef)entity).CreatedType.GetQualifiedId();
            }

            return "";
        }

        public static string GetTypeQualifiedId(this Entity entity) {
            if (entity is Function) {
                return ((Function)entity).Type.GetQualifiedId();
            } else if (entity is Variable) {
                return ((Variable)entity).Type.GetQualifiedId();
            } else if (entity is Struct) {
                return ((Struct)entity).Name;
            } else if (entity is Typedef) {
                return ((Typedef)entity).CreatedType.GetQualifiedId();
            }

            return "";
        }
    }
}
